﻿using System;
using System.Threading;
using System.Threading.Tasks;
using ssl;

namespace Sim
{
    public delegate void DbgInfo(string Msg);
    class SslApp
    {
        public void Start()
        {
            if (IntSim != null)
            {
                IntSim.Stop();
            }

            if (cancelSource != null)
            {
                cancelSource.Cancel();
            }
            if (Process != null)
            {
                Process = null;
            }
            Process = new SslProcess();

            cancelSource = new CancellationTokenSource();

            CusTask.creatTask = CreatSslTask;

            sptTask = new Task(() => { Process.SslMain(); }, cancelSource.Token, TaskCreationOptions.LongRunning);
            sptTask.Start();

            Thread.Sleep(1000);

            SampIntPeriodNs = 1000 * 1000 * 20 / Process.SslSampCntPerCircle();

            IntSim = new HardWareIntSim();
            IntSim.Start(SampInt, SampIntPeriodNs);

            Started = true;

        }

        public void Stop()
        {
            if (!Started)
            {
                return;
            }
            Started = false;

            cancelSource.Cancel();
            cancelSource = null;
            IntSim.Stop();
            IntSim = null;
            sptTask = null;
        }
        public void Suspend()
        {
            IntSim?.Suspend();
        }
        public void Resume()
        {
            IntSim?.Resume();
        }
        private void SampInt()
        {
            app1MsIntNsCnt += SampIntPeriodNs;

            if (app1MsIntNsCnt >= 1000 * 1000)
            {
                Process.Ssl1MsInt();
                app1MsIntNsCnt -= 1000 * 1000;
            }

            Process.SslSampInt();
        }
        private Task CreatSslTask(Action action)
        {
            Task task = new Task(action, cancelSource.Token, TaskCreationOptions.AttachedToParent);
            task.Start();
            return task;
        }
        
        public SslProcess Process { get; private set; }

        private HardWareIntSim IntSim;
        public bool Started { get; set; }
        public long SampIntPeriodNs { get; private set; }
        private long app1MsIntNsCnt;
        private Task sptTask;
        private CancellationTokenSource cancelSource; 
    }
    class HardWareIntSim
    {
        public delegate void IntDelegate();
        public void Start(IntDelegate cb,long Ns)
        {
            intperiod = Ns;
            IntCb = cb;
            if (SimThread != null)
            {
                Stop();
            }
            alive = true;
            suspend = false;
            SimThread = new Thread(ThreadLoop);
            SimThread.Priority = ThreadPriority.Highest;
            SimThread.Start();
        }
        public void Stop()
        {
            alive = false;
            SimThread.Abort();
            SimThread = null;
        }
        public void Suspend()
        {
            suspend = true;
        }
        public void Resume()
        {
            suspend = false;
        }
        public bool Alive() { return alive; }
        private void ThreadLoop()
        {
            
            NanoStopwatch NsNow = new NanoStopwatch();
            if (intperiod < 1000)
            {
                return;
            }
            long nextTime = NsNow.ElapsedNanoSeconds;
            while (alive)
            {
                if (suspend)
                {
                    Thread.SpinWait(10);
                    continue;
                }
                if (nextTime < NsNow.ElapsedNanoSeconds)
                {
                    Thread.SpinWait(10);
                    continue;
                }
                nextTime = NsNow.ElapsedNanoSeconds;
                IntCb();
            }
        }

        private bool alive;
        private bool suspend;
        private long intperiod;

        IntDelegate IntCb;
        private Thread SimThread;
    }


    public class NanoStopwatch : System.Diagnostics.Stopwatch
    {
        readonly double _nanoSecPerTick =
            1000*1000*1000D / System.Diagnostics.Stopwatch.Frequency;

        public NanoStopwatch()
        {
            if (!System.Diagnostics.Stopwatch.IsHighResolution)
            {
                throw new Exception("On this system the high-resolution " +
                                    "performance counter is not available");
            }
        }

        public long ElapsedNanoSeconds
        {
            get
            {
                return (long)(ElapsedTicks * _nanoSecPerTick);
            }
        }
    }

}
